<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue之模板引擎</title>
</head>
<body>
    <div id="content"></div>
    <script>
        // 模版字符串
        let tempStr = `
            <div>
                <ul class="ul_style">
                    [[$students]]
                    <li>
                        学生[[name]]的爱好是：
                        <ol>
                            [[$hobbies]]
                            <li>[[item]]</li>
                            [[^hobbies]]
                        </ol>
                    </li>
                    [[^students]]
                </ul>
                <ol class="ol_style">
                    [[$animal]]
                    <li>
                        动物类型：[[type]]<br>
                        <span>喜欢[[like]]</span><br>
                        <span>其他[[color]]</span><br>
                    </li>
                    [[^animal]]
                </ol>
             </div>
        `;
        // 数据
        var data ={
            students:[
                {"name":"小明","hobbies":["游泳","打篮球"]},
                {"name":"小红","hobbies":["编程","遛狗狗"]},
                {"name":"小蓝","hobbies":["打羽毛球","听音乐","看书"]},
                {"name":"小率","hobbies":["旅游"]}
            ],
            animal: [
                {
                    type: '鸟',
                    like: '吃虫子',
                    color: '白色',
                },
                {
                    type: '狗',
                    like: '吃肉',
                    color: '黑色',
                },
                {
                    type: '猫',
                    like: '吃老鼠',
                    color: 'red',
                }
            ]
        };

        class New_templateEngine {
            constructor() {};
            render(tempStr, data) {
                // 第一步：将templateStr转成tokens
                let tokens = this.parseTemplateTokens(tempStr);
                // console.log('render=>tokens:', tokens);
                // 第二步：将tokens和data组合渲染回来
                let domStr = this.renderTemplate(tokens, data);
                // 返回结果str
                return domStr;
            };
            parseTemplateTokens(tempStr) {
                // 实例一个扫描器
                let scanner= new Scanner(tempStr);
                let beforeTokens = [];
                // 开始扫描
                while(!scanner.eos()) {
                    let word = '';
                    word = scanner.scanUtil('[[');
                    if (word !== '') {
                        beforeTokens.push(['text', this.delSpace(word)]);
                    };
                    scanner.scan('[[');
                    word = scanner.scanUtil(']]');
                    if (word != '') {
                        if (word[0] === '$') {
                        beforeTokens.push(['$', word.substring(1)]);
                        }
                        else if (word[0] === '^') {
                            beforeTokens.push(['^', word.substring(1)]);
                        }
                        else {
                            beforeTokens.push(['name', word]);
                        };
                    };
                    scanner.scan(']]');
                };
                // console.log('parseTemplateTokens:', beforeTokens);
                return this.nestTokens(beforeTokens);
            };
            nestTokens(beforeTokens) {
                let nestTokens = [];
                // 栈 
                let sections = [];
                let collector = nestTokens;
                for(let i = 0; i < beforeTokens.length; i++) {
                    let token = beforeTokens[i];
                    switch (token[0]) {
                        case '$':
                            collector.push(token);
                            sections.push(token);
                            collector = token[2] = [];
                        break;
                        case '^':
                            sections.pop();
                            collector = sections.length > 0 ? sections[sections.length - 1][2] : nestTokens;
                        break;
                        default:
                            collector.push(token);
                        break;
                    }
                };
                // console.log('nestTokens:', nestTokens);
                return nestTokens;
            };
            renderTemplate(tokens, data) {
                // 结果字符串
                let tempStr = '';
                for (let i = 0; i < tokens.length; i++) {
                    let token = tokens[i];
                    if (token[0] == 'text') {
                        tempStr += token[1];
                    }
                    else if (token[0] == 'name') {
                        tempStr += this.lookup(token[1], data);
                    }
                    else if (token[0] == '$') {
                        tempStr += this.parseArray(token, data);
                    }
                };
                return tempStr;
            };
            parseArray(token, data) {
                let tempstr = '';
                let v = this.lookup(token[1], data);
                for (var i = 0; i < v.length; i++) {
                    tempstr += this.renderTemplate(token[2], {
                        ...v[i],
                        'item': v[i],
                    });
                };
                return tempstr;
            };
            delSpace(str) {
                let innnerDiv = false;
                let resultStr = '';
                for (let i = 0; i < str.length; i++) {
                    if (str[i] === '<') {
                        innnerDiv = true;
                    }
                    else if (str[i] === '>') {
                        innnerDiv = false;
                    }
                    if (!/\s+/.test(str[i])) {
                        resultStr += str[i];
                    }
                    else {
                        if (innnerDiv) {
                            resultStr += str[i];
                        }
                    }
                }
                return resultStr;
            };
            lookup(str, data) {
                // 避免点语法a.b.c
                let tempObj = data;
                if (str.indexOf('.') != -1 && str !== '.') {
                    let arr = str.split('.');
                    for (let i = 0; i < arr.length; i++) {
                        tempObj = tempObj[arr[i]];
                    }
                };
                return tempObj[str];
            }
        }
        // 扫描器类
        class Scanner {
            constructor(tempStr) {
                this.tempString = tempStr;
                // 指针
                this.pos = 0;
                // tempStr 剩余部分
                this.tail = tempStr;
            };
            // 扫描到特殊符号并返回扫描过的部分
            scanUtil(tag) {
                // 记录指针初始位置
                let startpos = this.pos;
                while(!this.eos() && this.tail.indexOf(tag) !== 0) {
                    this.pos ++;
                    this.tail = this.tempString.substring(this.pos);
                };
                return this.tempString.substring(startpos, this.pos);
            };
            // 跳过特殊符号
            scan(tag) {
                if (this.tail.indexOf(tag) === 0) {
                    let count = tag.length;
                    this.pos += count;
                    this.tail = this.tempString.substring(this.pos);
                }
            }
            // 是否边界
            eos() {
                return this.pos >= this.tempString.length;
            }
        }
        let templateEngine = new New_templateEngine();
        let dom = templateEngine.render(tempStr, data);
        console.log('dom:', dom);
        document.querySelector('#content').innerHTML = dom;
    </script>
</body>
</html>